#include "tetrixboard.h"

#include<QPainter>
#include<QKeyEvent>
#include<QLabel>

TetrixBoard::TetrixBoard(QWidget* parent)
    :QFrame(parent),
    is_started(false),
    is_pause(false)
{
    setFrameStyle(QFrame::Panel|QFrame::Sunken);
    setFocusPolicy(Qt::StrongFocus);

    clearBoard();

    this->next_piece.setRandomShape();
}

void TetrixBoard::setNextPieceLabe(QLabel *label)
{
    this->next_piece_labe=label;
}

QSize TetrixBoard::sizeHint() const
{
    return  QSize(BoardWidth*15+frameWidth()*2,BoardHeight*15+frameWidth()*2);
}

QSize TetrixBoard::minimumSizeHint() const
{
    return  QSize(BoardWidth*5+frameWidth()*2,BoardHeight*5+frameWidth()*2);
}

void TetrixBoard::start()
{
    if(is_pause)
        return;

    is_started=true;
    is_wait_after_line=false;
    score=0;

    clearBoard();

    emit scoreChanged(score);
    newPiece();
    timer.start(500,this);
}

void TetrixBoard::pause()
{
    if(!is_started) return;

    is_pause=!is_pause;
    if(is_pause)
        timer.stop();
    else
        timer.start(500,this);

    update();
}

void TetrixBoard::paintEvent(QPaintEvent *event)
{
    QFrame::paintEvent(event);

    QPainter painter(this);
    QRect rect=this->contentsRect();

    if(is_pause)
    {
        painter.drawText(rect,Qt::AlignCenter,"pause");
        return;
    }

    int boardTop=rect.bottom()-BoardHeight*squareHeight();

    for(int i=0;i<BoardHeight;++i)
    {
        for(int j=0;j<BoardWidth;++j)
        {
            TetrixShape shape=shapeAt(j,BoardHeight-i-1);
            if(shape!=NoShape)
            {
                drawSquare(painter,rect.left()+j*squareWidth(),boardTop+i*squareHeight(),shape);
            }
        }
    }


    if(cur_piece.shape()!=NoShape)
    {
        for(int i=0;i<4;++i)
        {
            int x=cur_x+cur_piece.x(i);
            int y=cur_y-cur_piece.y(i);

            drawSquare(painter,rect.left()+x*squareWidth(),boardTop+(BoardHeight-y-1)*squareHeight(),cur_piece.shape());
        }
    }
}

void TetrixBoard::keyPressEvent(QKeyEvent *event)
{
    if(!is_started||is_pause||cur_piece.shape()==NoShape)
    {
        QFrame::keyPressEvent(event);
        return;
    }

    switch(event->key())
    {
    case Qt::Key_Left:
        tryMove(cur_piece,cur_x-1,cur_y);
        break;
    case Qt::Key_Right:
        tryMove(cur_piece,cur_x+1,cur_y);
        break;
    case Qt::Key_Down:
        tryMove(cur_piece.rotatedRight(),cur_x,cur_y);
        break;
    case Qt::Key_Up:
        tryMove(cur_piece.rotatedLeft(),cur_x,cur_y);
        break;
    case Qt::Key_Space:
        dropDown();
        break;
    case Qt::Key_D:
         oneLineDown();
        break;

    default:
        QFrame::keyPressEvent(event);
    }
}

void TetrixBoard::timerEvent(QTimerEvent *event)
{
    if(event->timerId()==timer.timerId())
    {
        if(is_wait_after_line)
        {
            is_wait_after_line=false;
            newPiece();
            timer.start(500,this);
        }
        else
            oneLineDown();
    }
    else
        QFrame::timerEvent(event);
}

void TetrixBoard::clearBoard()
{
    for(int i=0;i<BoardWidth*BoardHeight;++i)
    {
        board[i]=NoShape;
    }
}

void TetrixBoard::newPiece()
{
    cur_piece=next_piece;
    next_piece.setRandomShape();
    showNextPiece();
    cur_x=BoardWidth/2+1;
    cur_y=BoardHeight-1+cur_piece.minY();

    if(!tryMove(cur_piece,cur_x,cur_y))
    {
        cur_piece.setShape(NoShape);
        timer.stop();
        is_started=false;
    }

}

void TetrixBoard::drawSquare(QPainter &painter, int x, int y, TetrixShape shape)
{
    static constexpr QRgb colorTable[8]{
        0x000000,0xcc6666,0x66cc66,0x6666cc,0xcccc66,0xcc66cc,0x66cccc,0xDAAA00
    };

    QColor color=colorTable[shape];
    painter.fillRect(x+1,y+1,squareWidth()-2,squareHeight()-2,color);

    painter.setPen(color.lighter());
    painter.drawLine(x,y+squareHeight()-1,x,y);
    painter.drawLine(x,y,x+squareWidth()-1,y);

    painter.setPen(color.darker());
    painter.drawLine(x+1,y+squareHeight()-1,x+squareWidth()-1,y+squareHeight()-1);
    painter.drawLine(x+squareWidth()-1,y+squareHeight()-1,x+squareWidth()-1,y+1);

}

bool TetrixBoard::tryMove(const TetrixPiece &newPiece, int newX, int newY)
{
    for(int i=0;i<4;++i)
    {
        int x=newX+newPiece.x(i);
        int y=newY-newPiece.y(i);
        if(x<0||x>=BoardWidth||y<0||y>=BoardHeight)
            return false;
        if(shapeAt(x,y)!=NoShape)
            return false;
    }

    cur_piece=newPiece;
    cur_x=newX;
    cur_y=newY;
    update();
    return true;
}

void TetrixBoard::dropDown()
{
    int dropHeight=0;
    int newY=cur_y;

    while (newY>0) {
        if(!tryMove(cur_piece,cur_x,newY-1))
            break;
        --newY;
        ++dropHeight;
    }

    pieceDropped(dropHeight);
}

void TetrixBoard::oneLineDown()
{
    if(!tryMove(cur_piece,cur_x,cur_y-1))
        pieceDropped(0);
}

void TetrixBoard::pieceDropped(int h)
{
    for(int i=0;i<4;++i)
    {
        int x=cur_x+cur_piece.x(i);
        int y=cur_y-cur_piece.y(i);
        shapeAt(x,y)=cur_piece.shape();
    }

    // score+=7+h;
    // emit scoreChanged(score);
    removeFullLines();

    if(!is_wait_after_line)
        newPiece();

}

void TetrixBoard::removeFullLines()
{
    int numFullLines=0;
    for(int i=BoardHeight-1;i>=0;--i)
    {
        bool isFull=true;
        for(int j=0;j<BoardWidth;++j)
        {
            if(shapeAt(j,i)==NoShape){
                isFull=false;
                break;
            }
        }

        if(isFull)
        {
            ++numFullLines;
            for(int k=i;k<BoardHeight-1;++k){
                for(int j=0;j<BoardWidth;++j)
                {
                    shapeAt(j,k)=shapeAt(j,k+1);
                }
            }

            for(int j=0;j<BoardWidth;++j)
            {
                shapeAt(j,BoardHeight-1)=NoShape;
            }
        }
    }

    if(numFullLines>0)
    {
        score+=10*numFullLines;
        emit scoreChanged(score);

        timer.start(500,this);
        is_wait_after_line=true;
        cur_piece.setShape(NoShape);
        update();
    }
}

void TetrixBoard::showNextPiece()
{
    if(this->next_piece_labe==nullptr)
        return;

    int dx=next_piece.maxX()-next_piece.minX()+1;
    int dy=next_piece.maxY()-next_piece.minY()+1;

    QPixmap pix(dx*squareWidth(),dy*squareHeight());
    QPainter painter(&pix);
    painter.fillRect(pix.rect(),next_piece_labe->palette().window());

    for(int i=0;i<4;++i)
    {
        int x=next_piece.x(i)-next_piece.minX();
        int y=next_piece.y(i)-next_piece.minY();

        drawSquare(painter,x*squareWidth(),y*squareHeight(),next_piece.shape());
    }

    next_piece_labe->setPixmap(pix);
}
